#include "include/hal_spi.h"
#include <iostream>

HAL::SPI_Master::SPI_Master(const std::string &dev, uint32_t speed, uint32_t mode, uint8_t bits)
{
    this->SPI_Init(dev, speed, mode, bits);
}
HAL::SPI_Master::SPI_Master(uint8_t port, uint8_t channel, uint32_t speed, uint32_t mode, uint8_t bits)
{
    this->SPI_Init("/dev/spidev" + std::to_string(port) + "." + std::to_string(channel), speed, mode, bits);
}

HAL::SPI_Master::~SPI_Master()
{
    if (fd >= 0)
    {
        close(this->fd);
        this->fd = -1;
    }
}

void HAL::SPI_Master::SPI_Init(const std::string &dev, uint32_t speed, uint32_t mode, uint8_t bits)
{
    this->dev = dev;
    this->fd = open(dev.c_str(), O_RDWR);
    if (this->fd < 0)
    {
        std::cerr << "open " << dev << " failed" << std::endl;
        return;
    }

    int ret = 0;
    ret = ioctl(this->fd, SPI_IOC_WR_MODE32, &mode);
    if (ret < 0)
    {
        std::cerr << "can't set spi send mode32" << std::endl;
        ret = ioctl(this->fd, SPI_IOC_WR_MODE, &mode);
        if (ret < 0)
        {
            std::cerr << "can't set spi mode" << std::endl;
            return;
        }
    }

    ret = ioctl(this->fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret < 0)
    {
        std::cerr << "can't set bits per word" << std::endl;
        return;
    }
    ret = ioctl(this->fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret < 0)
    {
        std::cerr << "can't set max speed hz" << std::endl;
        return;
    }

    this->xfer.speed_hz = speed;
    xfer.bits_per_word = bits;
    this->xfer.delay_usecs = 0;
    this->xfer.cs_change = 0;
    this->xfer.tx_nbits = 1;
    this->xfer.rx_nbits = 1;
}

int HAL::SPI_Master::WriteRead(uint8_t const *tx, uint8_t const *rx, size_t len)
{
    std::lock_guard<std::mutex> lock(this->mtx);
    if (this->fd < 0)
    {
        std::cerr << "fd is not open" << std::endl;
        return -1;
    }
    this->xfer.tx_buf = reinterpret_cast<unsigned long>(tx);
    this->xfer.rx_buf = reinterpret_cast<unsigned long>(rx);
    this->xfer.len = len;
    int ret = ioctl(this->fd, SPI_IOC_MESSAGE(1), &this->xfer);
    if (ret < 0)
    {
        std::cerr << "can't send spi message" << std::endl;
        return -1;
    }
    return ret;
}
